package com.berwin.syncer.network;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.berwin.syncer.entity.SharedText;
import com.berwin.syncer.entry.Syncer;
import com.berwin.syncer.listener.ServerListener;
import com.berwin.syncer.listener.SocketEventListener;
import com.berwin.syncer.monitor.SystemClipboardMonitor;
import com.berwin.syncer.utils.ClipboardUtility;
import com.berwin.syncer.utils.Utility;
import com.sun.security.ntlm.Server;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TextServer implements Runnable, SocketEventListener {

    private int port;
    private java.net.ServerSocket serverSocket;
    private boolean isRunning;
    private SocketEventListener listener;
    private ServerListener serverListener;
    private Map<String, Session> sessions;
    private List<SharedText> texts;

    private SystemClipboardMonitor monitor;

    public TextServer(int port, SystemClipboardMonitor monitor) {
        this.monitor = monitor;
        this.sessions = new HashMap<>();
        this.texts = new ArrayList<>();
        this.port = port;
    }

    public void start(SocketEventListener listener, ServerListener serverListener) {
        this.listener = listener;
        this.serverListener = serverListener;
        new Thread(this).start();
    }

    public void stop() {
        if (this.serverSocket != null) {
            try {

                for (Map.Entry<String, Session> entry : sessions.entrySet()) {
                    String key = entry.getKey();
                    Session session = entry.getValue();
                    session.stop();
                }
                this.sessions.clear();
                this.serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                this.serverSocket = null;
                this.isRunning = false;
                System.out.println("文本同步服务已停止");
            }
        }
    }

    public String getIPAddress() {
        String ip = Utility.getInet4Address().toString();
        return ip.replace("/", "");
    }

    public String getQRCodeText() {
        return String.format("%s-%d", this.getIPAddress(), this.getPort());
    }

    public int getPort() {
        return this.port;
    }

    public boolean isRunning() {
        return isRunning;
    }

    public void setRunning(boolean running) {
        isRunning = running;
    }

    @Override
    public void run() {
        System.out.println("文本服务器在:" + this.port + " 端口监听");
        try {
            this.serverSocket = new java.net.ServerSocket(this.port);
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.isRunning = true;
        while (this.isRunning) {
            try {
                if (serverListener != null)
                    serverListener.onStarted(this);
                Socket socket = this.serverSocket.accept();
                System.out.println("文本服务链接:" + socket.getInetAddress().getHostAddress());
                Session session = new Session(socket, this);
                session.start();
            } catch (IOException e) {
//                e.printStackTrace();
                System.out.println("文本服务意外退出，是否手动停服？");
                this.isRunning = false;
            }
        }
        try {
            if (this.serverSocket != null)
                this.serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.serverSocket = null;
        if (serverListener != null)
            serverListener.onStoped(this);
    }

    /**
     * 广播消息
     *
     * @param data          发送的消息
     * @param sender        消息发送者
     * @param excludeSender 是否同时也发给发送者
     */
    public void broadcast(JSONObject data, String sender, boolean excludeSender) {
        String message = data.toJSONString();
        System.out.println("广播消息:" + message);
        for (Map.Entry<String, Session> entry : sessions.entrySet()) {
            String key = entry.getKey();
            if (sender != null && excludeSender && key.equals(sender))
                continue;
            Session session = entry.getValue();
            session.send(message);
        }
        // 添加到缓存列表中
        if (data.getInteger("type") == Session.MSG_TYPE_SYNC_TEXT) {
            SharedText sharedText = SharedText.parseFromJSONObject(data);
            this.texts.add(0, sharedText);
            this.tryTextsCheckOverflow();
        }
    }

    @Override
    public void onConnect(Session session) {
        this.sessions.put(session.getDevice(), session);
        System.out.println("设备连接:" + session.getDevice());
        this.listener.onConnect(session);
        // 同步文本数据
        JSONObject obj = new JSONObject();
        obj.put("type", Session.MSG_TYPE_SYNC_TEXTS);
        JSONArray array = new JSONArray();
        for (int i = this.texts.size() - 1; i >= 0; i--) {
            SharedText text = this.texts.get(i);
            JSONObject item = text.toJSONObject();
            array.add(item);
        }
        obj.put("texts", array);
        session.send(obj.toJSONString());
    }

    @Override
    public void onMessage(Session session, JSONObject data) {
        this.broadcast(data, session.getDevice(), false);

        int type = data.getInteger("type");
        if (type == Session.MSG_TYPE_SYNC_TEXT) {
            SharedText sharedText = SharedText.parseFromJSONObject(data);
            // 写入到剪切板
            this.monitor.setLastClipboard(sharedText.getContent());
            ClipboardUtility.setSysClipboardText(sharedText.getContent());
        } else if (type == Session.MSG_TYPE_SYNC_FILE) {
            // 同步文件
        }
        this.listener.onMessage(session, data);
    }

    private void tryTextsCheckOverflow() {
        while (this.texts.size() > Syncer.MAX_CACHE_ITEM) {
            this.texts.remove(this.texts.size() - 1);
        }
    }

    @Override
    public void onDisconnect(Session session) {
        if (this.sessions.containsKey(session.getDevice())) {
            this.sessions.remove(session.getDevice());
            System.out.println("设备断开:" + session.getDevice());
        }
        this.listener.onDisconnect(session);
    }

    public Map<String, Session> getSessions() {
        return sessions;
    }

    public void setSessions(Map<String, Session> sessions) {
        this.sessions = sessions;
    }

    public SystemClipboardMonitor getMonitor() {
        return monitor;
    }

    public void setMonitor(SystemClipboardMonitor monitor) {
        this.monitor = monitor;
    }

    public List<SharedText> getTexts() {
        return texts;
    }

    public void setTexts(List<SharedText> texts) {
        this.texts = texts;
    }

}
